package com.donger.business.datascope;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
import com.donger.auth.component.UserDetail;
import com.donger.auth.utils.SecurityUtil;
import com.donger.business.enums.DataScopeTypeEnum;
import com.donger.common.core.constant.SecurityConstants;
import com.donger.common.core.exception.BizException;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.security.core.GrantedAuthority;

import javax.sql.DataSource;
import java.sql.Connection;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 数据权限拦截器
 *
 * @author lhl
 */
@Slf4j
@AllArgsConstructor
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataScopeInterceptor extends AbstractSqlParserHandler implements Interceptor {

    private final DataSource dataSource;

    @Override
    @SneakyThrows
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        this.sqlParser(metaObject);
        // 判断是否时查询语句
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
            return invocation.proceed();
        }
        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        String originalSql = boundSql.getSql();
        Object parameterObject = boundSql.getParameterObject();

        //查找参数中包含DataScope类型的参数
        DataScope dataScope = findDataScopeObject(parameterObject);
        if (dataScope == null) {
            return invocation.proceed();
        }

        String scopeName = dataScope.getScopeName();
        List<Long> deptIds = dataScope.getDeptIds();


        if (CollUtil.isEmpty(deptIds)) {
            UserDetail user = SecurityUtil.getUser();
            if (user == null) {
                throw new BizException("auto datascope, set up security details true");
            }

            List<String> roleIdList = user.getAuthorities()
                    .stream().map(GrantedAuthority::getAuthority)
                    .filter(authority -> authority.startsWith(SecurityConstants.ROLE))
                    .map(authority -> authority.split("_")[1])
                    .collect(Collectors.toList());

            Entity query = new Entity();
            if (CollectionUtil.isNotEmpty(roleIdList)) {
                query = Db.use(dataSource)
                        .query("SELECT * from sys_role where role_id IN (" + CollUtil.join(roleIdList, ",") + ")")
                        .stream().min(Comparator.comparingInt(o -> o.getInt("ds_type"))).get();
            } else {
                throw new BizException("该用户为维护角色！请联系管理员！");
            }


            Integer currentDataScope = query.getInt("ds_type");

            // 全部数据
            if (DataScopeTypeEnum.All.getType() == currentDataScope) {
                return invocation.proceed();
            }

            // 自定义
            if (DataScopeTypeEnum.CUSTOM.getType() == currentDataScope) {
                String dsScope = query.getStr("ds_scope");
                deptIds.addAll(Arrays.stream(dsScope.split(","))
                        .map(Long::valueOf).collect(Collectors.toList()));
            }
            //  本级 及 子级
            if (DataScopeTypeEnum.OWN_CHILD_LEVEL.getType() == currentDataScope) {
                List<Long> deptIdList = Db.use(dataSource)
                        .findBy("sys_dept_relation", "ancestor", user.getDeptId())
                        .stream().map(entity -> entity.getLong("descendant"))
                        .collect(Collectors.toList());
                deptIds.addAll(deptIdList);
            }

            // 本级
            if (DataScopeTypeEnum.OWN_LEVEL.getType() == currentDataScope) {
                deptIds.add(user.getDeptId());
            }
        }

        String join = CollectionUtil.join(deptIds, ",");
        originalSql = "select * from (" + originalSql + ") temp_data_scope where temp_data_scope." + scopeName + " in (" + join + ")";
        metaObject.setValue("delegate.boundSql.sql", originalSql);
        return invocation.proceed();
    }

    /**
     * 生成拦截对象的代理
     *
     * @param target
     * @return
     */
    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    /**
     * mybatis 配置属性
     *
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {

    }


    /**
     * 查找参数是否包括DataScope对象
     *
     * @param parameterObj 参数列表
     * @return DataScope
     */
    private DataScope findDataScopeObject(Object parameterObj) {
        if (parameterObj instanceof DataScope) {
            return (DataScope) parameterObj;
        } else if (parameterObj instanceof Map) {
            for (Object val : ((Map<?, ?>) parameterObj).values()) {
                if (val instanceof DataScope) {
                    return (DataScope) val;
                }
            }
        }
        return null;
    }
}
